home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
et
/
et3_0-a1.lha
/
et3
/
src
/
ShellTView.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-08-05
|
16KB
|
721 lines
#ifdef __GNUG__
#pragma implementation
#endif
#include "ShellTView.h"
#include "Class.h"
#include "Iterator.h"
#include "OrdColl.h"
#include "ByteArray.h"
#include "StyledText.h"
#include "System.h"
#include "String.h"
#include "Alert_e.h"
#include "Menu.h"
#include "MenuBar.h"
#include "Window.h"
#include "WindowSystem.h"
#include "Math.h"
char *gDefaultArgv[] = { "/bin/csh", 0 };
const int cPttyBufSize = 8192,
cHighWaterMark = 50000,
cLowWaterMark = 4000,
cESC = 27;
//---- output cursor (reversed caret) --------------------------------------
const int crevCaretHeight = 4,
crevCaretWidth = 7;
static u_short revCaretBits[]= {
# include "images/revCaret.image"
};
static SmartBitmap gRevCaretImage(Point(crevCaretWidth,crevCaretHeight),
revCaretBits);
static RGBColor *gCaretColor;
//---- class PttyInputHandler --------------------------------------------------
class PttyInputHandler : public SysEvtHandler {
FILE *fp;
ShellTextView *tv;
public:
MetaDef(PttyInputHandler);
PttyInputHandler(FILE *f, ShellTextView *t) : SysEvtHandler(fileno(f))
{ fp= f; tv= t; }
void Notify(SysEventCodes, int)
{ tv->Received(); }
};
NewMetaImpl(PttyInputHandler, SysEvtHandler, (TP(tv)));
//---- ShellZombieHandler ------------------------------------------------------
class ShellZombieHandler : public SysEvtHandler {
ShellTextView *sh;
public:
MetaDef(ShellZombieHandler);
ShellZombieHandler(int pid, ShellTextView *t) : SysEvtHandler(pid)
{ sh= t;}
void Notify(SysEventCodes, int)
{ sh->SlaveDied(); }
};
NewMetaImpl(ShellZombieHandler, SysEvtHandler, (TP(sh)));
//---- class ShellTextView -----------------------------------------------------
NewMetaImpl(ShellTextView, TextView, (TP(outputCursor), TP(inputCursor),
TP(zombieHandler), TP(pttyinp), TB(pendingCR),
TB(doReveal), TP(noEchoBuffer), T(lastSubmit),
T(maxLogSize), T(state), TP(pttycstr),
TSP(argv), 0));
ShellTextView::ShellTextView(
EvtHandler *eh, Rectangle r, Text *t,
char *name, char **args, bool cons,
TextViewFlags fl, Point b
) : TextView(eh, r, t, TRUE, fl, b, eTViewLeft)
{
Init(TRUE, name, args, cons);
}
void ShellTextView::Init(bool reveal, char *name, char **args, bool cons)
{
doReveal= reveal;
outputCursor= new Mark(0, 0, eStateNone, (eMarkFlags)eMarkNone);
inputCursor= new Mark(0, 0, eStateNone, (eMarkFlags)eMarkNone);
linebuf= 0;
pendingCR= FALSE;
inReceive= FALSE;
maxLogSize= cHighWaterMark;
noEchoBuffer= new GapText(20);
inpQueue= new OrdCollection;
GetText()->AddMark(outputCursor);
GetText()->AddMark(inputCursor);
gSystem->Setenv("TERM", GetTerm());
gSystem->Setenv("TERMCAP", GetTermCap());
pttycon= 0;
Start(name, args, cons);
}
ShellTextView::~ShellTextView()
{
Term();
}
void ShellTextView::Term()
{
Object *op;
if (text) {
op= text->RemoveMark(outputCursor);
SafeDelete(op);
op= text->RemoveMark(inputCursor);
SafeDelete(op);
}
if (pttyinp) {
pttyinp->Remove();
pttyinp= 0;
}
if (zombieHandler) {
zombieHandler->Remove();
zombieHandler= 0;
}
PerformCommand(gResetUndo);
SafeDelete(pttycon);
SafeDelete(pttycstr);
SafeDelete(noEchoBuffer);
SafeDelete(inpQueue);
}
Command *ShellTextView::DoKeyCommand(int ch, Token token)
{
if (pttycon == 0)
return gNoChanges;
if (ModifiesReadOnly())
SetSelection(text->End(), text->End(), TRUE);
if (pttycon->RawOrCBreak()) {
char c= ch;
pttycon->SubmitToSlave(&c, 1);
lastSubmit= text->End();
if (pttycon->Echo())
return TextView::DoKeyCommand(ch, token);
return gResetUndo;
}
if (strchr(pttycstr,ch)) {
GapText buf(4096), *tosend;
byte *cbuf;
int n;
if (ch == pttyc.intr || ch == pttyc.quit) {
SubmitInterrupt(ch);
return gResetUndo;
}
if (pttycon->Echo()) {
buf.ReplaceRange(0, buf.End(), text, outputCursor->Pos(), text->End());
tosend= &buf;
} else
tosend= noEchoBuffer;
tosend->AddChar(tosend->End(), ch);
n= tosend->End();
cbuf= tosend->GetLineAccess(&linebuf, 0, n);
if (tosend == noEchoBuffer)
noEchoBuffer->Empty();
Submit((char*) cbuf, n);
return gNoChanges;
}
if (!pttycon->Echo()) {
noEchoBuffer->Append(ch);
return gNoChanges;
}
if (text->IsKindOf(StyledText) && ch != '\b') {
int f, t;
GetSelection(&f,&t);
StyledText *st= (StyledText*) text;
st->SetCharStyle(eTxTPFace, f, t,
CharStyleSpec(eFontDefault, eFaceBold, 0, 0, FALSE));
}
if (Iscntrl(ch) && ch != '\b' && ch != '\t')
return CntrlChar((byte)ch);
return TextView::DoKeyCommand(ch, token);
}
char *ShellTextView::GetTermCap()
{
return "etterm|etterm dumb terminal emulator:bs";
}
char *ShellTextView::GetTerm()
{
return "etterm";
}
void ShellTextView::Received()
{
char buf[cPttyBufSize];
int n= 0, f, t, upto;
Focus();
inReceive= TRUE;
GetSelection(&f, &t);
inputCursor->ChangeMark(f, t-f);
outputCursor->Unlock();
if (pttycon)
n= pttycon->Read(buf, sizeof(buf)-1);
if (n <= 0) // EOT from child or lost connection
SlaveDied();
else {
GapText gt(n);
HideSelection(FALSE);
SetNoBatch(pttycon->RawOrCBreak());
ProcessOutput(>, buf, n);
if (outputCursor->Pos() < lastSubmit)
upto= Math::Min(lastSubmit, text->End());
else
upto= outputCursor->Pos();
InsertReceivedText(>, outputCursor->Pos(), upto);
if (outputCursor->Pos() > maxLogSize)
Wrap();
SetSelection(inputCursor->Pos(), inputCursor->End());
}
if (doReveal) {
int p= outputCursor->Pos();
Rectangle r= BoundingRect(p, p).Expand(8);
RevealOutput(r, r.extent);
}
outputCursor->Lock();
PerformCommand(gResetUndo);
inReceive= FALSE;
FlushPendingInput();
}
void ShellTextView::FlushPendingInput()
{
if (inpQueue->Size()) {
ByteArray *bp;
Iter next(inpQueue);
while (bp= (ByteArray*)next()) {
TtyInput((char*)bp->Str());
inpQueue->RemovePtr(bp);
SafeDelete(bp);
}
}
}
void ShellTextView::InsertReceivedText(Text *t, int from, int to)
{
if (!text->IsKindOf(StyledText))
text->Paste(t, from, to);
else {
StyledText *st= (StyledText*) text;
int upto, ostart= outputCursor->Pos();
st= (StyledText*) text;
st->SetCharStyle(eTxTPFace, from, to, CharStyleSpec(eFontDefault, eFacePlain, 0));
text->Paste(t, from, to);
if (ostart <= lastSubmit && !pttycon->RawOrCBreak()) {
upto= Math::Min(Math::Min(lastSubmit, outputCursor->Pos()), text->End());
st->SetCharStyle(eTxTPFace, ostart, upto, CharStyleSpec(eFontDefault, eFaceBold,0));
st->SetCharStyle(eTxTPFace, upto, upto, CharStyleSpec(eFontDefault, eFacePlain,0));
}
}
}
void ShellTextView::ProcessOutput(GapText *gt, char *buf, int n)
{
for (char *p= buf; p < buf+n; p++)
if (HandleSpecialOutChar(*p, gt))
gt->AddChar(gt->End(), *p);
}
void ShellTextView::RevealOutput(Rectangle r, Point p)
{
RevealRect(r, p);
}
bool ShellTextView::HandleSpecialOutChar(char p, GapText *t)
{
if (state) {
state= FALSE;
return FALSE;
}
if (pendingCR) {
if (p == '\r')
return FALSE;
if (p == '\n')
pendingCR= FALSE;
else {
CarriageReturn(t);
pendingCR= FALSE;
}
}
switch (p) {
case cESC: // during a more(1) spurious ESC '9's appear
state= TRUE;
return FALSE;
case '\r':
pendingCR= TRUE;
break;
case '\b':
BackSpace(t, 1);
break;
case '\007':
gWindow->Bell();
break;
default:
return TRUE;
}
return FALSE;
}
void ShellTextView::Submit(char *buf, int n)
{
if (n && pttycon && pttycon->SubmitToSlave(buf, n))
ShowAlert(eAlertNote, "Pty command buffer overflow\nlast command ignored");
lastSubmit= text->End();
}
void ShellTextView::SubmitInterrupt(char ch)
{
if (pttycon && pttycon->SubmitToSlave(&ch, 1))
pttycon->SubmitToSlave(&ch, 1); // try again after flush of buffers
}
void ShellTextView::Done()
{
if (pttycon)
pttycon->KillChild();
}
bool ShellTextView::IsIdle()
{
return pttycon == 0;
}
void ShellTextView::SlaveDied()
{
if (pttycon)
CleanupDeath();
zombieHandler= 0;
}
void ShellTextView::CleanupDeath()
{
if (pttyinp) {
pttyinp->Remove();
pttyinp= 0;
}
ClearOutput();
SafeDelete(pttycon);
}
void ShellTextView::DrawOutputCursor(Point p)
{
Point hotspot(-crevCaretHeight, 0);
Rectangle r(p+hotspot, Point(7,4));
r.origin+= GetInnerOrigin();
if (GrHasColor()) {
if (gCaretColor == 0)
gCaretColor= new_RGBColor(0, 0, 255);
GrPaintBitMap(r, gRevCaretImage, gCaretColor);
} else
GrPaintBitMap(r, gRevCaretImage, ePatXor);
}
void ShellTextView::Draw(Rectangle r)
{
Point p;
int l;
// if (!pttycon)
// GrPaintRect(r, ePatGrey25);
TextView::Draw(r);
if (pttycon && pttycon->RawOrCBreak())
return;
CharToPoint(outputCursor->Pos(), &l, &p);
DrawOutputCursor(p);
}
void ShellTextView::Paste(Text *insert)
{
if (ModifiesReadOnly())
return;
if (pttycon && pttycon->RawOrCBreak()) {
AutoTextIter next(insert);
Token t;
int ch;
while ((ch= next()) != cEOT) {
t.Code= (short)ch;
DoKeyCommand((short)ch, t);
}
} else
TextView::Paste(insert);
}
bool ShellTextView::ModifiesReadOnly()
{
int f, t;
GetSelection(&f, &t);
return f < outputCursor->Pos();
}
bool ShellTextView::DeleteRequest(int from, int)
{
return from >= outputCursor->Pos();
}
void ShellTextView::BecomeConsole()
{
if (pttycon)
pttycon->BecomeConsole();
}
void ShellTextView::BackSpace(Text *t, int n)
{
int s= t->End();
if (s > 0) {
int d1= Math::Min(s, n);
t->Cut(s-d1, s);
}
if (n > s) {
int d2= n-s;
lastSubmit= Math::Max(0, lastSubmit-d2);
int p= outputCursor->Pos();
outputCursor->ChangeMark(Math::Max(0, p-d2), 0);
text->Cut(p-d2,p);
}
}
void ShellTextView::CarriageReturn(Text *t)
{
int s= t->End(), ch;
pendingCR= FALSE;
while (s > 0) {
int ch= (*t)[s];
if (ch == '\n' || ch == '\r') {
t->Cut(s+1,t->End());
return;
}
s--;
}
t->Empty();
s= text->End();
int at= s-1;
while (at >= 0) {
ch= (*text)[at];
if (ch == '\n' || ch == '\r') {
outputCursor->ChangeMark(at+1, 0);
text->Cut(at+1, s);
lastSubmit= at+1;
return;
}
at--;
}
}
Command *ShellTextView::CntrlChar(byte b)
{
GapText gt((byte*)"^");
gt.Append(b+'@');
return InsertText(>);
}
void ShellTextView::Wrap()
{
int line, del, at= outputCursor->Pos();
line= CharToLine(Math::Max(0, text->End()-cLowWaterMark));
del= StartLine(line);
lastSubmit-= del;
text->Cut(0, del);
}
Text *ShellTextView::SetText(Text *t, bool revealTop)
{
Text *ot= TextView::SetText(t, revealTop);
if (ot) {
ot->RemoveMark(outputCursor);
ot->RemoveMark(inputCursor);
}
t->AddMark(outputCursor);
t->AddMark(inputCursor);
MarksToEOT();
return ot;
}
void ShellTextView::MarksToEOT()
{
Text *t= GetText();
outputCursor->ChangeMark (t->End(), 0);
outputCursor->Lock();
inputCursor->ChangeMark (t->End(), 0);
lastSubmit= t->End();
}
void ShellTextView::ClearOutput()
{
text->Cut(0, text->End());
MarksToEOT();
SetSelection(0,0,FALSE);
RevealSelection();
}
void ShellTextView::DoReveal(bool b)
{
if (!doReveal && b)
RevealSelection();
doReveal= b;
}
void ShellTextView::Start(char *name, char **args, bool cons)
{
if (pttycon != 0)
Error("Start", "shell is already running");
state= 0;
MarksToEOT();
if (name == 0)
name= gDefaultArgv[0];
argv= args;
if (args == 0)
argv= gDefaultArgv;
pttycon= gSystem->MakePttyConnection(name, argv);
if (!pttycon)
Error("Start", "cannot establish connection with tty");
if (cons)
BecomeConsole();
if (pttycon->GetFile() == 0)
Error("Start", "could not spawn slave pseudo tty");
pttycon->GetPttyChars(&pttyc);
pttycstr= strprintf("\n\r%c%c%c%c%c", pttyc.rprnt, pttyc.susp,
pttyc.intr, pttyc.quit,
pttyc.eof);
SetStopChars(pttycstr);
gSystem->AddFileInputHandler(
pttyinp= new PttyInputHandler(pttycon->GetFile(), this));
gSystem->AddZombieHandler(
zombieHandler= new ShellZombieHandler(pttycon->GetPid(), this));
}
void ShellTextView::Reconnect()
{
if (pttycon) {
switch (ShowAlert(eAlertCaution, "There is alreay a shell connection.\nForce reconnect?")) {
case cIdYes:
pttycon= 0;
break;
case cIdNo:
case cIdCancel:
return;
}
}
Start(argv[0], argv);
ForceRedraw();
}
Menu *ShellTextView::MakeMenu(int menuId)
{
Menu *m;
if (menuId == cSHELLMENU) {
m= new Menu(cSHELLMENU, "Shell");
m->AppendItems(
"Auto Reveal On", cAUTOREVEAL,
"Reconnect", cRECONNECT,
"Become Console", cBECOMECONSOLE,
"Do It", cDOIT,
0);
}
else
m= TextView::MakeMenu(menuId);
return m;
}
void ShellTextView::DoSetupMenu(Menu *m)
{
char *current;
TextView::DoSetupMenu(m);
if (ModifiesReadOnly()) {
m->DisableItem(cCUT);
m->DisableItem(cPASTE);
}
if (doReveal)
current= "Auto reveal off";
else
current= "Auto reveal on";
m->ReplaceItem(cAUTOREVEAL, current);
if (pttycon)
m->EnableItem(cBECOMECONSOLE);
m->EnableItem(cRECONNECT);
m->EnableItems(cCLEAR, cAUTOREVEAL, 0);
if (!Caret())
m->EnableItem(cDOIT);
}
Command *ShellTextView::DoMenuCommand(int cmd)
{
switch (cmd) {
case cAUTOREVEAL:
DoReveal(!doReveal);
break;
case cCLEAR:
ClearOutput();
return gResetUndo;
case cRECONNECT:
Reconnect();
break;
case cBECOMECONSOLE:
pttycon->BecomeConsole();
break;
case cDOIT:
Doit();
return gResetUndo;
}
return TextView::DoMenuCommand(cmd);
}
void ShellTextView::SelectAll(bool redraw)
{
SetSelection(outputCursor->Pos(), text->End(), redraw);
RevealSelection();
}
void ShellTextView::Doit()
{
int f, t;
GapText buf(256);
Token token;
GetSelection(&f, &t);
text->Copy(&buf,f, t);
SetSelection(text->End(), text->End());
Paste(&buf);
DoKeyCommand('\n', token);
}
char **ShellTextView::GetArgv()
{
return argv;
}
PttyConnection *ShellTextView::GetPtty()
{
return pttycon;
}
void ShellTextView::SetTtySize(Rectangle r, Font *fd)
{
int cols= r.extent.x / fd->Width(' ');
Metric m= MeasureText(0, fd, r.extent.x, 1);
int rows= r.extent.y / m.Height();
SetTtySize(rows, cols);
}
void ShellTextView::ChangedViewRect(Rectangle r)
{
SetTtySize(r, text->GetFont(0));
if (Width() != r.Width())
SetWidth(r.Width());
}
void ShellTextView::SetTtySize(int rows, int cols)
{
if (pttycon)
pttycon->SetSize(rows, cols);
}
void ShellTextView::SetMaxLogSize(int n)
{
maxLogSize= n;
}
Command *ShellTextView::TtyInput(char *buf, int len)
{
Token t;
if (inReceive) {
inpQueue->Add(new ByteArray(buf, len));
return gNoChanges;
}
Command *cmd= gNoChanges;
if (len == -1)
len= strlen(buf);
for (char *cp= buf; cp < buf+len; cp++) {
t.Code= (short)*cp;
cmd= DoKeyCommand((int)*cp, t);
}
PerformCommand(gResetUndo);
return cmd;
}
OStream& ShellTextView::PrintOn(OStream &s)
{
TextView::PrintOn(s);
return s << doReveal SP;
}
IStream& ShellTextView::ReadFrom(IStream &s)
{
TextView::ReadFrom(s);
Term();
s >> Bool(doReveal);
Init(doReveal, gDefaultArgv[0], gDefaultArgv, FALSE);
MarksToEOT();
return s;
}